use std::error::Error;

use super::{Entry, entry, WildcardEntry};
use std::env;
use std::path::Path;
use std::fmt;

pub struct Classpath {
    boot_classpath: Box<dyn Entry>,
    ext_classpath: Box<dyn Entry>,
    user_classpath: Box<dyn Entry>,
}

impl Classpath {
    pub fn parse(jre_option: &str, cp_option: &str) -> Classpath {
        Classpath { 
            boot_classpath: parse_boot_classpath(jre_option),
            ext_classpath: parse_ext_classpath(jre_option), 
            user_classpath: parse_user_classpath(cp_option)
        }
    }
}

impl Entry for Classpath {
    // 读取文件字节码
    // className: fully/qualified/ClassName
    fn read_class(&self, class_name: &str) -> Result<Vec<u8>, Box<dyn Error>> {
        let class_name = class_name.to_string() + ".class";
                // 读取boot classpath
        if let Ok(data) = self.boot_classpath.read_class(class_name.as_str()) {
                        return Ok(data);
        }
        // 读取ext classpath
        if let Ok(data) = self.ext_classpath.read_class(class_name.as_str()) {
                        return Ok(data);
        }
        // 读取user classpath
        if let Ok(data) = self.user_classpath.read_class(class_name.as_str()) {
                        return Ok(data);
        }
        Err(("not found class, class_name: ".to_string() + class_name.as_str()).into())
    }

    fn string(&self) -> String {
        self.user_classpath.string()
    }
}

// 格式化boot classpath
fn parse_boot_classpath(jre_option: &str) -> Box<dyn Entry>{
    let jre_dir = get_jre_dir(jre_option);
    // jre/lib/*
    let path = Path::new(jre_dir.as_str()).join("lib").join("*");
    Box::new(WildcardEntry::new(path.to_str().unwrap()))
}

// 格式化ext classpath
fn parse_ext_classpath(jre_option: &str) -> Box<dyn Entry>{
    let jre_dir = get_jre_dir(jre_option);
    // jre/lib/*
    let path = Path::new(jre_dir.as_str()).join("lib").join("ext").join("*");
    Box::new(WildcardEntry::new(path.to_str().unwrap()))
}

// 格式化user classpath
fn parse_user_classpath(cp_option: &str) -> Box<dyn Entry>{
    if cp_option.is_empty() {
        entry::new_entry(".")
    } else {
        entry::new_entry(cp_option)
    }
}

// 获取jre dir
fn get_jre_dir(jre_option: &str) -> String {
    // 存在jre_option
    if !jre_option.is_empty() && exists(jre_option) {
        return jre_option.to_string();
    }
    // 当前目录下存在jre则返回
    if exists("./jre") {
        return "./jre".to_string();
    }
    // 查找环境变量
    if let Ok(java_home) = env::var("JAVA_HOME") {
        let java_home = java_home.as_str();
        if exists(java_home) {
            let path = Path::new(java_home).join("jre");
            return path.to_str().unwrap().to_string();
        }
    }
    panic!("Can not find jre folder!");
}

// 路径是否存在
fn exists(path: &str) -> bool {
    Path::new(path).exists()
}

impl fmt::Display for Classpath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.string())
    }
}

